#include <coroutine>
#include <ranges>
#include <iostream>
class Task
{
public:
    //协程必须带一个promise_type的内部类，可以用using或者typedef定义
    struct promise_type
    {
        /*
            在未进入协程方法前调用此方法获得父对象
            可以认为是在协程环境初始化时调用
        */
        Task get_return_object()  
        {
            return {this};
        }
        /*
            //协程初始化失败后调用此方法 在内存不足时可能初始化失败
        Task get_return_object_on_allocation_failure()noexcept     //
        {
        }
        */

        /*
            协程初始化完调用此方法
            返回 std::suspend_never{} 表示不切出协程（立即执行的意思）
            返回 std::suspend_always{} 表示切出协程（不要立即执行）
            这两个类在 co_waait 的时候解释
        */
        auto initial_suspend()
        {
            return std::suspend_never{};
        }

        /*
        //在使用 co_return 返回void时调用此方法
        void return_void()
        {}
        */
        /*
        //在使用 co_return 返回时调用此方法 参数为返回的值 
        */
        void return_value(int i)
        {
            m_retunValue=i;isReturn=true;
        }
        /*
            在方法返回后，上下文环境销毁前调用此方法，返回值同 initial_suspend
        */
        auto final_suspend() noexcept
        {
            return std::suspend_never{};
        }
        /*
            协程方法抛出没有处理的错误调用此方法
        */
        void unhandled_exception(){}
        /*
            调用co_yeild时调用此方法 参数是co_yield的值
        */
        auto yield_value(int v)
		{
			m_yieldValue = v;
			return std::suspend_always{};
		}
    private:
        friend Task;
        int m_retunValue;
        int m_yieldValue;
        bool isReturn=false;
    };

    using Handle = std::coroutine_handle<promise_type>;

    Task(promise_type *p)
    :m_p(p)
    {
        if(p)
        {
            m_handle=Handle::from_promise(*p); //可以通过promise对象获取协程句柄
        }
    }

    void remuse()
    {
        m_handle.resume();
    }
    int getYieldValue()
    {return m_p->m_yieldValue;}
    int getReturnValue()
    {return m_p->m_retunValue;}
    bool isReturn()
    {return m_p->isReturn;}
private:
    Handle m_handle;
    promise_type *m_p;
};


Task cofun(int n)
{
    std::cout<<"in cofun\n";
    for(auto i:std::ranges::views::iota(0,n))
    {
        co_yield i;
    }
    co_return n;
   
}


int main(int argn,char** argv)
{
    Task task = cofun(10);
    while (1)
    {
        if(task.isReturn())
        {
            std::cout<<"return "<<task.getReturnValue()<<std::endl;
            break;
        }else
        {
            std::cout<<"yield "<<task.getYieldValue()<<std::endl;
            task.remuse();
        }
    }
    return 0;
}